#include "GlDxAudioManager.h"

#include "UtilityAssert.h"

namespace AudioLibrary
{
	//--------------------------------------------
	DxAudioManager::DxAudioManager(HWND hwnd)
		: mDriver(0)
	{
		HRESULT res = DirectSoundCreate8(0, &mDriver, 0);
		ASSERT(res == DS_OK, "Could not init sound driver.");
		mDriver->SetCooperativeLevel(hwnd, DSSCL_NORMAL);
	}

	//--------------------------------------------
	DxAudioManager::~DxAudioManager()
	{
		if(mDriver)
			mDriver->Release();
	}

	//--------------------------------------------
	void DxAudioManager::playSound(const char *fileName)
	{
		IDirectSoundBuffer8 *buffer = getBuffer(fileName);
		if(buffer)
		{
			buffer->SetVolume(DSBVOLUME_MAX);
			buffer->SetCurrentPosition(0);
			buffer->Play(0, 0, 0);
		}
	}

	struct WavHeader
	{
		BYTE	RIFF[4];        // RIFF Header      Magic header
		UINT	ChunkSize;      // RIFF Chunk Size  
		BYTE	WAVE[4];        // WAVE Header      
		BYTE	fmt[4];         // FMT header       
		UINT	Subchunk1Size;  // Size of the fmt chunk                                
		USHORT	AudioFormat;    // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM 
		USHORT	NumOfChan;      // Number of channels 1=Mono 2=Sterio                   
		UINT	SamplesPerSec;  // Sampling Frequency in Hz                             
		UINT	bytesPerSec;    // bytes per second 
		USHORT	blockAlign;     // 2=16-bit mono, 4=16-bit stereo 
		USHORT	bitsPerSample;  // Number of bits per sample      
		BYTE	Subchunk2ID[4]; // "data"  string   
		UINT	Subchunk2Size;  // Sampled data length
	};

	//--------------------------------------------
	IDirectSoundBuffer8* DxAudioManager::getBuffer(const char *fileName)
	{
		NameBufferMap::iterator i = mBuffers.find(fileName);
		IDirectSoundBuffer8 *buffer = 0;
		if(i == mBuffers.end())
		{
			FILE *f = fopen(fileName, "rb");
			if(f)
			{
				WavHeader header;
				void *data = 0;

				size_t r = fread(&header, sizeof(WavHeader), 1, f);
				ASSERT(r == 1, "Corrupted wav header.");
				data = malloc(header.Subchunk2Size);
				r = fread(data, header.Subchunk2Size, 1, f);
				ASSERT(r == 1, "Corrupted wav file.");

				fclose(f);

				WAVEFORMATEX wavFormat;
				wavFormat.wFormatTag = WAVE_FORMAT_PCM;
				wavFormat.nChannels = header.NumOfChan;
				wavFormat.nSamplesPerSec = header.SamplesPerSec;
				wavFormat.nBlockAlign = header.blockAlign;
				wavFormat.nAvgBytesPerSec = header.bytesPerSec;
				wavFormat.wBitsPerSample = header.bitsPerSample;

				DSBUFFERDESC desc;
				desc.dwSize = sizeof(DSBUFFERDESC);
				desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN;
				desc.dwBufferBytes = header.Subchunk2Size;
				desc.dwReserved = 0;
				desc.lpwfxFormat = &wavFormat;
				desc.guid3DAlgorithm = DS3DALG_DEFAULT;

				IDirectSoundBuffer *dBuffer = 0;
				HRESULT res = mDriver->CreateSoundBuffer(&desc, &dBuffer, 0);
				ASSERT(res == DS_OK, "Could not create buffer.");
				res = dBuffer->QueryInterface(IID_IDirectSoundBuffer8, reinterpret_cast<void**>(&buffer));
				dBuffer->Release();
				ASSERT(res == S_OK, "Could not convert IDirectSoundBuffer to IDirectSoundBuffer8.");

				void *writePointer = 0;
				DWORD maxWriteSize = 0;
				res = buffer->Lock(
					0,
					0,
					&writePointer,
					&maxWriteSize,
					0,
					0,
					DSBLOCK_ENTIREBUFFER
				);
				ASSERT(res == DS_OK, "Could not map buffer to memory.");
				memcpy(writePointer, data, maxWriteSize);
				res = buffer->Unlock(writePointer, maxWriteSize, 0, 0);
				ASSERT(res == DS_OK, "Could not write data to buffer.");
				
				free(data);
				mBuffers[fileName] = buffer;
			}
		}
		else
		{
			buffer = i->second;
		}
		return buffer;
	}
}
